#include<errno.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<ctype.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<assert.h>
#include<string.h>
#define NUM 1024
#define OPT_NUM 64

#define NONE_REDIR 0
#define INPUT_REDIR 1
#define APPEND_REDIR 2
#define OUTPUT_REDIR 3
#define trimSpace(start){\
    while(isspace(*start))  start++;\
}while(0)

char LineCommand[NUM];
char* myargv[OPT_NUM];
int lastcode = 0;
int lastsign = 0;
int redirtype = NONE_REDIR;
char* redirfile = NULL;
void CheckCommand(char*commands)
{
  assert(commands);
  char* start = commands;
  char* end = commands + strlen(commands);
  while(start < end)
  {
    if(*start == '>')
    {
        *start = '\0';
        start++;
        if(*start == '>')
        {
          //append redir
          redirtype = APPEND_REDIR;
          start++;
        }
        else
        {
          redirtype = OUTPUT_REDIR;
        }
        trimSpace(start);
        redirfile = start;
    }
    else if(*start == '<')
    {
      *start = '\0';
      start++;
      trimSpace(start);
      //填写重定向信息
      redirtype = INPUT_REDIR;
      redirfile = start;
    }
    else
    {
      start++;
    }
  }
}
#define MAX_PATH 1024

void print_prompt() {
    char prompt[MAX_PATH];
    char *username = getenv("USER"); // 获取当前用户名
    if (username == NULL) {
        perror("getenv failed");
        exit(EXIT_FAILURE);
    }
    
    char *homedir = getenv("HOME"); // 获取当前用户的主目录
    if (homedir == NULL) {
        perror("getenv failed");
        exit(EXIT_FAILURE);
    }
    
    char cwd[MAX_PATH];
    if (getcwd(cwd, sizeof(cwd)) == NULL) { // 获取当前工作目录
        perror("getcwd failed");
        exit(EXIT_FAILURE);
    }
    
    // 检查当前目录是否为用户的主目录，以便在提示符中显示波浪号("~")
    char *rel_path;
    if (strncmp(cwd, homedir, strlen(homedir)) == 0) {
        rel_path = cwd + strlen(homedir);
        sprintf(prompt, "[%s@localhost:~%s$] ", username, rel_path);
    } else {
        rel_path = cwd;
        sprintf(prompt, "[%s@localhost:%s$] ", username, rel_path);
    }
    
    printf("%s", prompt);
}
int main()
{
  while(1)
  {
    //initialize
    redirtype = NONE_REDIR;
    redirfile = NULL;
    print_prompt();
    //printf("[用户名@主机名 当前路径]");
    fflush(stdout);
    //获取用户输入,用户输入时一定会输入一个\n
    char* s = fgets(LineCommand , sizeof(LineCommand)-1 , stdin);
    (void)s;
    LineCommand[strlen(LineCommand) - 1] = 0;
    CheckCommand(LineCommand);
    //printf("test:%s\n" , LineCommand);
    //因为要输入选项例如ls -al ， 因此我们需要切割字符串
    myargv[0] = strtok(LineCommand , " ");
    //如果没有子串 ， strtok会返回NULL
    int i = 1;
    if(myargv[0] != NULL && strcmp(myargv[0] , "ls") == 0)
    {
      myargv[i] = "--color=auto";
      i++;
    }
    while(myargv[i++] = strtok(NULL , " "));
    if(myargv[0] != NULL && strcmp(myargv[0] , "cd") == 0)
    {
      if(myargv[1] != NULL)  chdir(myargv[1]);
      if(strcmp(myargv[1] , "~") == 0) chdir(getenv("HOME"));
      continue;
    }
    if(myargv[0] != NULL && myargv[1] != NULL && strcmp(myargv[0], "echo") == 0)
    {
       if(strcmp(myargv[1] , "$?") == 0)
       {
         printf("%d\n" , lastsign);
         continue;
       }
       else{
         printf("%s\n" , myargv[1]);
         continue;
       }
    }
#ifdef DEBUG
    for(int i = 0 ; myargv[i] ; i++)
    {
      printf("%s" , myargv[i]);
    }
  
#endif
    //执行命令
    pid_t id = fork();
    assert(id != -1);
    if(id == 0)
    {
       //因为命令是子进程执行的，所以真正执行重定向的还是子进程
      switch(redirtype)
      {
        case NONE_REDIR:
          //do nothing
             break;
        case INPUT_REDIR:
             {
               int fd = open(redirfile , O_RDONLY);//input redirect need the file exist
               if(fd < 0)
               {
                 perror("open");
                 exit(1);
               }
               dup2(fd , 0);
               break;
             }
        case APPEND_REDIR:
        case OUTPUT_REDIR:
             {
               int flags = O_WRONLY | O_CREAT;
               if(redirtype == APPEND_REDIR) flags |= O_APPEND;
               else flags |= O_TRUNC;
               int fd = open(redirfile , flags , 0666);
               if(fd < 0)
               {
                 perror("open");
                 exit(1);
               }
               dup2(fd , 1);
               break;
             }
        default:
             break;
      }
      execvp(myargv[0] , myargv);
       exit(10);
    }
    int status = 0;
    pid_t ret = waitpid(id , &status , 0);
    assert(ret > 0);
    lastcode = ((status>>8)&0xFF);
    lastsign = (status & 0x7F);
  } 
  
  return 0;
}
